#ifndef AVALON_TUPLEMESSAGE_HPP
#define AVALON_TUPLEMESSAGE_HPP
#include <boost/call_traits.hpp>
#include <boost/tuple/tuple.hpp>
#include "Avalon/Base/Template.hpp"
#include "Avalon/Serialization/DataShuttle.hpp"
#include "Avalon/Serialization/ShuttleTuple.hpp"
#include "Avalon/Services/Message.hpp"

namespace Avalon {
namespace Services {
  template<typename T, typename A0 = NullType, typename A1 = NullType,
    typename A2 = NullType, typename A3 = NullType, typename A4 = NullType,
    typename A5 = NullType, typename A6 = NullType, typename A7 = NullType,
    typename A8 = NullType, typename A9 = NullType, typename A10 = NullType>
  class TupleMessage {};

  #define PARAMETER_TYPE(z, n, q)                                              \
    BOOST_PP_COMMA_IF(n) typename boost::call_traits<A##n>::param_type

  #define DECLARE_PARAMETER(z, n, q)                                           \
    BOOST_PP_COMMA_IF(n) typename boost::call_traits<A##n>::param_type a##n

  #define GET_TUPLE_ELEMENT(z, n, q)                                           \
    BOOST_PP_COMMA_IF(n) boost::get<n>(m_tuple)

  #define LIST_PARAMETERS(z, n, q)                                             \
    BOOST_PP_COMMA_IF(n) a##n

  #define BOOST_PP_LOCAL_MACRO(n)                                              \
  template<typename T BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, typename A)>\
  class TupleMessage<T BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, A)>        \
      : public Message {                                                       \
    public:                                                                    \
      typedef T Name;                                                          \
      typedef boost::function<void (ServiceProtocol*                           \
        BOOST_PP_COMMA_IF(n) BOOST_PP_REPEAT(n, PARAMETER_TYPE,))> Slot;       \
                                                                               \
      static int InitializeMessage(const char* name) {                         \
        Serialization::DataShuttle::RegisterType<TupleMessage>(name, 0);       \
        return 0;                                                              \
      }                                                                        \
                                                                               \
      BOOST_PP_IF(n, TupleMessage() {},)                                       \
                                                                               \
      TupleMessage(BOOST_PP_REPEAT(n, DECLARE_PARAMETER, A))                   \
          BOOST_PP_IF(n, : m_tuple(BOOST_PP_REPEAT(n, LIST_PARAMETERS, a)),) {}\
                                                                               \
    protected:                                                                 \
      virtual void EmitSignal(boost::function_base* slot,                      \
          ServiceProtocol* protocol) {                                         \
        (*static_cast<Slot*>(slot))(protocol BOOST_PP_COMMA_IF(n)              \
          BOOST_PP_REPEAT(n, GET_TUPLE_ELEMENT,));                             \
      }                                                                        \
                                                                               \
      virtual void Shuttle(Serialization::DataShuttle* shuttle,                \
          unsigned int version) {                                              \
        BOOST_PP_IF(n, shuttle->Shuttle("elements", m_tuple);,)                \
      }                                                                        \
                                                                               \
    private:                                                                   \
      friend class Serialization::DataShuttle;                                 \
      BOOST_PP_IF(n, (boost::tuple<BOOST_PP_ENUM_PARAMS(n, A)>) m_tuple;,)     \
  };

  #define BOOST_PP_LOCAL_LIMITS (0, AVALON_SERVICE_PARAMETERS)
  #include BOOST_PP_LOCAL_ITERATE()
  #undef PARAMETER_TYPE
  #undef DECLARE_PARAMETER
  #undef GET_TUPLE_ELEMENT
  #undef LIST_PARAMETERS

  #define AVALON_DECLARE_MESSAGE(Type, Name, ...)                              \
    struct Type##Token {};                                                     \
    typedef ::Avalon::Services::TupleMessage<Type##Token, ##__VA_ARGS__> Type; \
    namespace {                                                                \
      int Type##AVALON_UNIQUE_NAME = Type::InitializeMessage(Name);            \
    }
}
}

#endif // AVALON_TUPLEMESSAGE_HPP
